package boardgamer.yavalath.client.threads;

import android.util.Log;
import boardgamer.common.client.AbstractActivity;
import boardgamer.common.client.AbstractView;
import boardgamer.common.client.Logger;
import boardgamer.common.net.PlayerId;
import boardgamer.yavalath.common.YavalathGameServer;
import boardgamer.yavalath.common.ai.UCTAi;
import com.google.common.collect.Maps;

import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static boardgamer.common.client.Logger.log;
import static boardgamer.common.client.Logger.logMethod;
import static boardgamer.yavalath.client.threads.GameThread.GameState.STATE_PAUSE;
import static boardgamer.yavalath.client.threads.GameThread.GameState.STATE_READY;
import static boardgamer.yavalath.client.threads.GameThread.GameState.STATE_RUN;
import static boardgamer.yavalath.client.threads.GameThread.GameState.STATE_TERMINATED;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

public class GameThread {

  public static enum GameState {
    STATE_UNINITIALIZED,
    STATE_READY,
    STATE_PAUSE,
    STATE_RUN,
    STATE_TERMINATED,
  }

  private final ExecutorService executorService;
  private final AbstractView<?> view;
  private final AbstractActivity<?> activity;

  private GameState gameState;

  private Map<PlayerId, AiThread> playerMap;

  public GameThread(AbstractView<?> view, AbstractActivity<?> activity) {
    this.executorService = Executors.newCachedThreadPool();
    this.view = checkNotNull(view, "view");
    this.activity = checkNotNull(activity, "activity");
    this.playerMap = Maps.newHashMap();
    this.gameState = STATE_READY;
  }

  public void startGame() {
    logMethod();
    checkState(gameState != STATE_RUN, "App is already running.");

    if (gameState == GameState.STATE_READY) {
      // Kick off the UI thread.
      executorService.submit(new UiThread(view, this));

      // Kick off the Update thread.
      executorService.submit(new UpdateThread(view, activity, this));
    }
    setGameState(STATE_RUN);
  }

  @Deprecated
  public void startAiThreads(Iterable<? extends PlayerId> aiPlayers, YavalathGameServer yavalathGameServer) {
    // Kick of AiThreads
    // TODO(sfraim) move this
    for (PlayerId playerId : aiPlayers) {
      playerMap.put(playerId, new AiThread(playerId, yavalathGameServer, new UCTAi()));
      executorService.submit(playerMap.get(playerId));
    }
  }

  public void pause() {
    setGameState(STATE_PAUSE);
  }

  public void terminateGameThread() {
    checkState(gameState != STATE_TERMINATED, "App has already been shutdown.");
    boolean retry = true;
    while (retry) {
      try {
        executorService.shutdownNow();
        setGameState(STATE_TERMINATED);
        retry = false;
      } catch (CancellationException e) {
        Logger.log(Log.ERROR, "CancellationException");
        retry = false;
      } catch (Exception e) {
        log(Log.ERROR, e.toString());
      }
    }
  }

  private void setGameState(GameState state) {
    Logger.log(Log.ERROR, "Setting GameState to: " + state);
    gameState = state;
  }

  public GameState getGameState() {
    return gameState;
  }
}